﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Text;

namespace JWTAuthorize
{
    /// <summary>
    /// jwt的中间件，分别在网关、身份认证服务和api的项目中通过依赖注入的方式调用
    /// .net core中好多组件功能都是单独写成中间件，如果要使用这些功能，以依赖注入的方式来使用
    /// </summary>
    public static class JwtProviderMiddleWare
    {
        /// <summary>
        /// 在网关中依赖注入
        /// </summary>
        /// <param name="services">startup中的IServiceCollection对象</param>
        /// <param name="issuer">发行人</param>
        /// <param name="audience">订阅人</param>
        /// <param name="secret">密钥</param>
        /// <param name="defaultScheme">默认架构</param>
        /// <param name="isHttps">是否https</param>
        /// <returns></returns>
        public static AuthenticationBuilder AddGateWayJwtBearer(this IServiceCollection services, string issuer, string audience, string secret, string defaultScheme, bool isHttps = false)
        {
            var keyByteArray = Encoding.ASCII.GetBytes(secret);
            var signingKey = new SymmetricSecurityKey(keyByteArray);
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,
                ValidateIssuer = true,
                ValidIssuer = issuer,
                ValidateAudience = true,
                ValidAudience = audience,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero,
                RequireExpirationTime = true,
            };
            return services.AddAuthentication(options =>
            {
                options.DefaultScheme = defaultScheme;
            })
             .AddJwtBearer(defaultScheme, opt =>
             {
                 //不使用https，生成环境中要设置为true,开启HTTPS的安全传输
                 opt.RequireHttpsMetadata = isHttps;
                 opt.TokenValidationParameters = tokenValidationParameters;
             });
        }

        /// <summary>
        /// 在各个业务api项目中依赖注入
        /// </summary>
        public static AuthenticationBuilder AddAPIPolicyJwtBearer(this IServiceCollection services, string issuer, string audience, string secret, string defaultScheme, string policyName, string deniedUrl, bool isHttps = false)
        {

            var keyByteArray = Encoding.ASCII.GetBytes(secret);
            var signingKey = new SymmetricSecurityKey(keyByteArray);
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,
                ValidateIssuer = true,
                ValidIssuer = issuer,//发行人
                ValidateAudience = true,
                ValidAudience = audience,//订阅人
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero,
                RequireExpirationTime = true,

            };
            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
            var customRequirement = new CustomRequirement(
               deniedUrl,
                ClaimTypes.Role,
                issuer,
                audience,
                signingCredentials,
                expiration: TimeSpan.FromHours(10)
                );
            //依赖注入授权JwtProviderHandler,三种方式的依赖注入，这里是单例的,具体是哪三种模式可以百度
            services.AddSingleton<IAuthorizationHandler, JwtProviderHandler>();
            services.AddSingleton(customRequirement);
            return services.AddAuthorization(options =>
            {
                options.AddPolicy(policyName,
                          policy => policy.Requirements.Add(customRequirement));

            })
         .AddAuthentication(options =>
         {
             options.DefaultScheme = defaultScheme;
         })
         .AddJwtBearer(defaultScheme, o =>
         {
             //不使用https
             o.RequireHttpsMetadata = isHttps;
             o.TokenValidationParameters = tokenValidationParameters;
         });
        }
        /// <summary>
        /// 在身份验证服务中依赖注入调用，身份验证服务可以单独分开，也可以和网关是同一个项目，对于大系统
        /// 一般都是分开，作为单独的身份认证服务器
        /// </summary>
        public static IServiceCollection AddJWTCreate(this IServiceCollection services, string issuer, string audience, string secret, string deniedUrl)
        {
            var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secret)), SecurityAlgorithms.HmacSha256);
            var customeRequirement = new CustomRequirement(
               deniedUrl,
                ClaimTypes.Role,
                issuer,
                audience,
                signingCredentials,
                expiration: TimeSpan.FromHours(10)
                );
            return services.AddSingleton(customeRequirement);

        }
    }
}
